package com.engine.jucailinkq.attendance.attendanceanalysis.cmd;

import com.engine.jucailinkq.attendance.attendanceanalysis.dto.clockpoint.ClockPointDTO;
import com.engine.jucailinkq.attendance.enums.*;
import com.engine.common.biz.AbstractCommonCommand;
import com.engine.common.entity.BizLogContext;
import com.engine.jucailinkq.common.util.DateUtil;
import com.engine.jucailinkq.common.util.Utils;
import com.engine.core.interceptor.CommandContext;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import weaver.general.Util;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 计算出勤时长
 */
@Slf4j
public class ComputeAttendanceDurationCmd extends AbstractCommonCommand<Map<String,Object>> {

    public ComputeAttendanceDurationCmd(Map<String,Object> params){
        this.params=params;
    }
    @Override
    public BizLogContext getLogContext() {
        return null;
    }

    @Override
    public Map<String, Object> execute(CommandContext commandContext) {
        Map<String,Object> resultMap = Maps.newHashMap();
        //排班
        List<Map<String, Object>> scheduleResult = (List<Map<String, Object>>)params.get("scheduleResult");
        //请假记录
        List<Map<String,Object>> askForLeaveList = (List<Map<String,Object>>)params.get("askForLeaveList");
        //请假项目
        Map<String,Map<String,Object>> askForLeaveItems = (Map<String,Map<String,Object>>)params.get("askForLeaveItems");
        //外出记录
        List<Map<String,Object>> evectionList = (List<Map<String,Object>>)params.get("evectionList");
        //外出项目
        Map<String,Map<String,Object>> evectionItems = (Map<String,Map<String,Object>>)params.get("evectionItems");
        //请假-抵消异常
        Map<Map<String, Object>,List<Map<String, Object>>> askForLeaveToOffsetAbnomaly = (Map<Map<String, Object>,List<Map<String, Object>>>)params.get("askForLeaveToOffsetAbnomaly");
        //出差-抵消异常
        Map<Map<String, Object>,List<Map<String, Object>>> evectionToOffsetAbnomaly = (Map<Map<String, Object>,List<Map<String, Object>>>)params.get("evectionToOffsetAbnomaly");
        //分析日期
        String analysisDate = Util.null2String(params.get("analysisDate"));
        //考勤项目
        List<Map<String,Object>> attendanceItems = (List<Map<String,Object>>)params.get("attendanceItems");
        //经过请假外出处理过的异常项目
        List<Map<String,Object>> abnormalClockInList = (List<Map<String,Object>>)params.get("abnormalClockInList");
        //单位小时
        double edsc = Double.valueOf(scheduleResult.get(0).get("edsc").toString());
        if (0 == edsc){
            resultMap.put("attendanceDuration",edsc);
            return resultMap;
        }

        //漏打卡异常小时数
        double misscardHour = 0;
        //异常时间
        double abnormalMinute = 0;
        /**
         * 当工作时间段存在异常项目时
         */
        abnormalClockInList = abnormalClockInList.stream().filter(e->e.get("bdlx").equals(ClassSegmentTypeEnum.WORK_TIME.getKey()) || e.get("bdlx").equals(ClassSegmentTypeEnum.ASK_FOR_LEAVE.getKey()) || e.get("bdlx").equals(ClassSegmentTypeEnum.EVECTION.getKey())).collect(Collectors.toList());


        for (Map<String, Object> scheduleMap :scheduleResult){
            if (ClassSegmentTypeEnum.WORK_TIME.getKey().equals(scheduleMap.get("bdlx"))){
                int edfzs = Integer.valueOf(scheduleMap.get("edfzs").toString());
                List<Map<String,Object>> list = Utils.getAbnormalListBySchedule(scheduleMap,abnormalClockInList,analysisDate);
                double totalTime=0;
                for (Map<String,Object> abnormal:list){
                    AttendanceItemTypeEnum itemType = (AttendanceItemTypeEnum)abnormal.get("itemType");
                    String kczgsc = Util.null2String(abnormal.get("kczgsc"));
                    if (!CheckBoxEnum.CHECKED.getKey().equals(kczgsc)){
                        continue;
                    }
                    if (itemType == AttendanceItemTypeEnum.MISSE_CARD){
                        misscardHour +=Double.valueOf(scheduleMap.get("edxss").toString());
                        totalTime = 0;
                        break;
                    }else {
                        int betweenMinutes = Integer.valueOf(Util.null2String(abnormal.get("betweenMinutes")==null?"0":abnormal.get("betweenMinutes")));
                        if (betweenMinutes > edfzs){
                            betweenMinutes = edfzs;
                        }
                        String item = Util.null2String(abnormal.get("item"));
                        List<Map<String,Object>> abnormalItemList = attendanceItems.stream().filter(e->e.get("key").equals(item)).collect(Collectors.toList());
                        if (abnormalItemList.size() > 0){
                            //起步扣除分钟数
                            int qbkcsc = Integer.valueOf(Util.null2String(abnormalItemList.get(0).get("qbkcsc")));
                            //超出后单次累加扣除分钟数
                            int cckcbc = Integer.valueOf(Util.null2String(abnormalItemList.get(0).get("cckcbc")));
                            if (betweenMinutes <= qbkcsc){
                                betweenMinutes = qbkcsc;
                            }else {
                                int deductionDuration = betweenMinutes-qbkcsc;
                                betweenMinutes = Double.valueOf(Utils.getItemduration(cckcbc,AccountingUnitEnum.MINUTES.getKey(),deductionDuration,AccountingUnitEnum.MINUTES,Double.valueOf(scheduleResult.get(0).get("edsc").toString()))).intValue()+qbkcsc;
                            }
                            totalTime+=betweenMinutes;
                        }
                    }
                }
                abnormalMinute+=totalTime;
            }
        }
        /**
         * 当存在请假时，考虑扣除请假时长
         */
        double askForLeaveTime = removeAskForLeave(analysisDate,scheduleResult,askForLeaveList,askForLeaveItems,abnormalClockInList,askForLeaveToOffsetAbnomaly);

        /**
         * 当存在外出时，考虑扣除外出时长
         */
        double evectionTimeTime = removeEvection(analysisDate,scheduleResult,evectionList,evectionItems,abnormalClockInList,evectionToOffsetAbnomaly);

        log.debug("misscardHour : {},abnormalMinute : {}, : {},evectionTimeTime:{}",misscardHour,abnormalMinute,askForLeaveTime,evectionTimeTime);
        double deductTime = Double.valueOf(String.format ("%.2f",(abnormalMinute+askForLeaveTime+evectionTimeTime)/60));
        edsc = edsc-misscardHour-deductTime;
        if (edsc < 0){
            edsc=0;
        }

        resultMap.put("attendanceDuration",String.format ("%.2f", edsc));
        return resultMap;
    }

    public double removeAskForLeave(String analysisDate,List<Map<String, Object>> scheduleResult,List<Map<String,Object>> askForLeaveList,
                                    Map<String,Map<String,Object>> askForLeaveItems,List<Map<String, Object>> abnormalClockInList,
                                    Map<Map<String, Object>,List<Map<String, Object>>> askForLeaveToOffsetAbnomaly){
        double employTime = 0;
        double edsc = Double.valueOf(scheduleResult.get(0).get("edsc").toString());
        //半天规则
        String btgz = Util.null2String(scheduleResult.get(0).get("btgz"));

        Map<String,List<Map<String,Object>>> askForLeaveGroupMap = askForLeaveList.stream().filter(e->!"".equals(Util.null2String(e.get("kssj"))) && !"".equals(Util.null2String(e.get("jssj"))))
                .collect(Collectors.groupingBy(e->Util.null2String(e.get("ksrq"))+Util.null2String(e.get("kssj"))+"&"+Util.null2String(e.get("jsrq"))+Util.null2String(e.get("jssj"))));
        List<Map<String,Object>> filterAskForLeaveList = Lists.newArrayList();
        askForLeaveGroupMap.entrySet().forEach(e->{
            if (e.getValue().size() > 1){
                filterAskForLeaveList.add(e.getValue().get(0));
            }else if (e.getValue().size() == 1){
                filterAskForLeaveList.addAll(e.getValue());
            }
        });

        for (int i=0;i<askForLeaveList.size();i++){
            //请假时长
            String qjsc = Util.null2String(askForLeaveList.get(i).get("qjsc"));
            //请假项目
            Map<String,Object> askForLeaveItem = askForLeaveItems.get(askForLeaveList.get(i).get("jqlx"));
            //半天请假
            String btj = Util.null2String(askForLeaveList.get(i).get("btj"));
            //作用时段
            String zysd = Util.null2String(askForLeaveItem.get("zysd"));
            //消除异常
            List<Map<String, Object>> offsetAbnomaly = askForLeaveToOffsetAbnomaly.get(askForLeaveList.get(i));
            if (!zysd.contains(WorkForTimeEnum.WORK_TIME.getKey()) && !WorkForTimeEnum.ALL_TIME.getKey().equals(zysd)){
                continue;
            }

            if ("".equals(Util.null2String(askForLeaveList.get(i).get("kssj"))) || "".equals(Util.null2String(askForLeaveList.get(i).get("jssj")))){
                //弹性请假,半天请假
                if (CheckBoxEnum.CHECKED.getKey().equals(btj) && (btgz.equals(HalfDayRuleREnum.BY_CLASS_SET.getKey()) || btgz.equals(HalfDayRuleREnum.FIXED_DURATION.getKey()))){
                    //半天请假
                    if (offsetAbnomaly != null && offsetAbnomaly.size() > 0){
                        Set<Map<String,Object>> schedules = Utils.getScheduleByAbnormalList(scheduleResult,offsetAbnomaly,analysisDate);
                        if (schedules.size() > 0){
                            employTime += schedules.stream().mapToDouble(e->Double.valueOf(e.get("edfzs").toString())).sum();
                        }else {
                            employTime +=edsc*30;
                        }
                    }else {
                        employTime +=edsc*30;
                    }
                }else if (!"".equals(qjsc)){
                    //时长请假
                    if (offsetAbnomaly != null && offsetAbnomaly.size() > 0){
                        List<Map<String, Object>> list = offsetAbnomaly.stream().filter(e->ClassSegmentTypeEnum.WORK_TIME.getKey().equals(e.get("bdlx"))).collect(Collectors.toList());
                        employTime += Math.round(list.size()/Double.valueOf(offsetAbnomaly.size()) *Double.valueOf(qjsc)*60);
                    }else {
                        employTime +=Double.valueOf(qjsc)*60;
                    }
                }
            }
        }
        for (int i=0;i<filterAskForLeaveList.size();i++){
            //请假项目
            Map<String,Object> askForLeaveItem = askForLeaveItems.get(filterAskForLeaveList.get(i).get("jqlx"));
            //作用时段
            String zysd = Util.null2String(askForLeaveItem.get("zysd"));
            if (!zysd.contains(WorkForTimeEnum.WORK_TIME.getKey()) && !WorkForTimeEnum.ALL_TIME.getKey().equals(zysd)){
                continue;
            }

            //按照开始时间，结束时间请假
            for (Map<String, Object> scheduleMap:scheduleResult){
                if (ClassSegmentTypeEnum.WORK_TIME.getKey().equals(scheduleMap.get("bdlx"))){
                    String dtkssj = Utils.getkssjTime(scheduleMap,analysisDate);
                    String dtjssj = Utils.getjssjTime(scheduleMap,analysisDate);

                    List<Map<String,Object>> list = Utils.getAbnormalListBySchedule(scheduleMap,abnormalClockInList,analysisDate).stream().filter(e->e.get("itemType")==AttendanceItemTypeEnum.MISSE_CARD).collect(Collectors.toList());
                    if (list.size() == 0){
                        //存在漏卡时不计入请假时长，防止重复累加
                        employTime +=Utils.getStartAndEndTime(dtkssj,dtjssj,filterAskForLeaveList.get(i));
                    }
                }
            }
        }
        return employTime;
    }

    public double removeEvection(String analysisDate,List<Map<String, Object>> scheduleResult,List<Map<String,Object>> evectionList,
                                    Map<String,Map<String,Object>> evectionItems,List<Map<String, Object>> abnormalClockInList,
                                 Map<Map<String, Object>,List<Map<String, Object>>> evectionToOffsetAbnomaly){
        double employTime = 0;
        double edsc = Double.valueOf(scheduleResult.get(0).get("edsc").toString());
        //半天规则
        String btgz = Util.null2String(scheduleResult.get(0).get("btgz"));

        Map<String,List<Map<String,Object>>> evectionGroupMap = evectionList.stream().filter(e->!"".equals(Util.null2String(e.get("kssj"))) && !"".equals(Util.null2String(e.get("jssj"))))
                .collect(Collectors.groupingBy(e->Util.null2String(e.get("ksrq"))+Util.null2String(e.get("kssj"))+"&"+Util.null2String(e.get("jsrq"))+Util.null2String(e.get("jssj"))));
        List<Map<String,Object>> filterEvectionList = Lists.newArrayList();
        evectionGroupMap.entrySet().forEach(e->{
            if (e.getValue().size() > 1){
                filterEvectionList.add(e.getValue().get(0));
            }else if (e.getValue().size() == 1){
                filterEvectionList.addAll(e.getValue());
            }
        });

        for (int i=0;i<evectionList.size();i++){
            //出差时长
            String ccsc = Util.null2String(evectionList.get(i).get("ccsc"));
            //出差时长分钟数
            int ccscMinute = "".equals(ccsc)?0:Double.valueOf(Math.ceil(Double.valueOf(ccsc)*60)).intValue();
            //出差项目
            Map<String,Object> evectionItem = evectionItems.get(evectionList.get(i).get("cclx"));
            //半天出差
            String btcc = Util.null2String(evectionList.get(i).get("btcc"));
            //作用时段
            String zysd = Util.null2String(evectionItem.get("zysd"));
            //消除异常
            List<Map<String, Object>> offsetAbnomaly = evectionToOffsetAbnomaly.get(evectionList.get(i));

            if (!zysd.contains(WorkForTimeEnum.WORK_TIME.getKey()) && !WorkForTimeEnum.ALL_TIME.getKey().equals(zysd)){
                continue;
            }

            if ("".equals(Util.null2String(evectionList.get(i).get("kssj"))) || "".equals(Util.null2String(evectionList.get(i).get("jssj")))){
                //半天出差
                if (CheckBoxEnum.CHECKED.getKey().equals(btcc) && (btgz.equals(HalfDayRuleREnum.BY_CLASS_SET.getKey()) || btgz.equals(HalfDayRuleREnum.FIXED_DURATION.getKey()))){
                    //半天出差
                    if (offsetAbnomaly != null && offsetAbnomaly.size() > 0){
                        Set<Map<String,Object>> schedules = Utils.getScheduleByAbnormalList(scheduleResult,offsetAbnomaly,analysisDate);
                        if (schedules.size() > 0){
                            employTime += schedules.stream().mapToDouble(e->Double.valueOf(e.get("edfzs").toString())).sum();
                        }else {
                            employTime +=edsc*30;
                        }
                    }else {
                        employTime +=edsc*30;
                    }
                }else if (!"".equals(ccsc)){
                    //时长出差
                    if (offsetAbnomaly != null && offsetAbnomaly.size() > 0){
                        List<Map<String, Object>> list = offsetAbnomaly.stream().filter(e->ClassSegmentTypeEnum.WORK_TIME.getKey().equals(e.get("bdlx"))).collect(Collectors.toList());
                        employTime += Math.round(list.size()/Double.valueOf(offsetAbnomaly.size()) *Double.valueOf(ccsc)*60);
                    }else {
                        employTime +=Double.valueOf(ccsc)*60;
                    }
                }
            }
        }
        for (int i=0;i<filterEvectionList.size();i++){
            //出差项目
            Map<String,Object> evectionItem = evectionItems.get(filterEvectionList.get(i).get("cclx"));
            //作用时段
            String zysd = Util.null2String(evectionItem.get("zysd"));
            if (!zysd.contains(WorkForTimeEnum.WORK_TIME.getKey()) && !WorkForTimeEnum.ALL_TIME.getKey().equals(zysd)){
                continue;
            }
            //按照开始时间，结束时间出差
            for (Map<String, Object> scheduleMap:scheduleResult){
                String dtkssj = Utils.getkssjTime(scheduleMap,analysisDate);
                String dtjssj = Utils.getjssjTime(scheduleMap,analysisDate);
                if (ClassSegmentTypeEnum.WORK_TIME.getKey().equals(scheduleMap.get("bdlx"))){

                    List<Map<String,Object>> list = Utils.getAbnormalListBySchedule(scheduleMap,abnormalClockInList,analysisDate).stream().filter(e->e.get("itemType")==AttendanceItemTypeEnum.MISSE_CARD).collect(Collectors.toList());
                    if (list.size() == 0){
                            //存在漏卡时不计入出差时长，防止重复累加
                        employTime = employTime + Utils.getStartAndEndTime(dtkssj,dtjssj,filterEvectionList.get(i));
                    }
                }
            }
        }
        return employTime;
    }
}
